<?php
/**
 * File Cache Tool.
 */
class FileCache
{
    private $xattrMaxLength = 4000;
    private $xattrKey = 'data';
    private $prefixLength = 4;
    private $dbPath = './data/';
    private $dbExt = '.dat';
    private $dir = '';
    private $tableName = '';

    public function __construct(string $table = '')
    {
        if (!xattr_supported(__FILE__)) {
            die("Not support xattr");
        }
        $this->setTableName($table);
    }

    public function get(int $id = 0)
    {
        $path = $this->calcPath($id);
        $content = $this->getContent($path);
        return $content[$id] ?? [];
    }

    public function set(int $id = 0, array $data = [])
    {
        $path = $this->calcPath($id);
        $content = $this->getContent($path);
        $content[$id] = $data;
        $content = $this->encode($content);
        return $this->write($path, $content);
    }

    private function calcPath(int $id = 0)
    {
        $prefix = substr(md5($id), 0, $this->getPrefixLength());
        $prefix = implode('/', str_split($prefix));
        $this->setDir($this->getDbPath() . $prefix . '/');
        return $this->getDir() . $this->getTableName() . $this->getDbExt();
    }

    private function getContent(string $path = '')
    {
        $content = $this->read($path);
        $content = $this->decode($content);
        return $content;
    }

    private function read(string $path = '')
    {
        if (!is_file($path)) {
            return '';
        }
        $data = xattr_get($path, $this->getXattrKey());
        return $data !== FALSE ? $data : file_get_contents($path);
    }

    private function write(string $path = '', string $data = '')
    {
        if (strlen($data) > $this->getXattrMaxLength()) {
            xattr_remove($path, $this->getXattrKey());
            return file_put_contents($path, $data);
        }
        is_file($path) || $this->touch($path);
        return xattr_set($path, $this->getXattrKey(), $data);
    }

    private function touch(string $path = '')
    {
        is_dir($this->getDir()) || mkdir($this->getDir(), 0755, TRUE);
        touch($path);
    }

    private function encode(array $data = [])
    {
        return igbinary_serialize($data);
    }

    private function decode(string $string = '')
    {
        if ($string === '') {
            return [];
        }
        return igbinary_unserialize($string);
    }

    public function getXattrMaxLength()
    {
        return $this->xattrMaxLength;
    }

    public function setXattrMaxLength(int $length = 4000)
    {
        $this->xattrMaxLength = $length;
    }

    public function getXattrKey()
    {
        return $this->xattrKey;
    }

    public function setXattrKey(string $xattr_key = '')
    {
        $this->xattrKey = $xattr_key;
    }

    public function getPrefixLength()
    {
        return $this->prefixLength;
    }

    public function setPrefixLength(int $length = 4)
    {
        $this->prefixLength = $length;
    }

    public function getDbPath()
    {
        return $this->dbPath;
    }

    public function setDbPath(string $db_path = '')
    {
        $this->dbPath = $db_path;
    }

    public function getDbExt()
    {
        return $this->dbExt;
    }

    public function setDbExt(string $ext = '')
    {
        $this->dbExt = $ext;
    }

    public function getDir()
    {
        return $this->dir;
    }

    public function setDir(string $dir = '')
    {
        $this->dir = $dir;
    }

    public function getTableName()
    {
        return $this->tableName;
    }

    public function setTableName(string $table = '')
    {
        $this->tableName = $table;
    }
}

function createRandData(int $max = 100000)
{
    $data = [];
    for ($i=0; $i < $max; $i++) { 
        $data[] = [
            'id' => $i,
            'username' => substr(md5($i), 1, 7),
            'password' => md5($i),
            'mobile' => 18100000000+$i,
            'status' => rand(0, 1),
            'last_login_time' => time()+rand(1,100000),
            'last_login_ip' => sprintf('%s.%s.%s.%s', rand(1,255), rand(1,255), rand(1,255), rand(1,255)),
            'login_count' => rand(1,100000),
            'create_time' => time()+rand(1,100000),
            'update_time' => time()+rand(1,100000),
        ];
    }
    return $data;
}
